
namespace ShoppingCart.DataAccess
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Common;
    using Microsoft.Practices.EnterpriseLibrary.Data;
    using ShoppingCart.Business.Entity;

    /// <summary>
    /// Represents the DataAccess of PedidoData.
    /// </summary>
    public class PedidoData : Singleton<PedidoData>
    {
        #region Attributes

        private readonly Database BaseDatos = DatabaseFactory.CreateDatabase();

        #endregion

        #region Methods

        /// <summary>
        /// Selects all records from the Pedido table by a foreign key.
        /// </summary>
        public Pedido Obtener(int idPedido)
        {
            var pedido = default(Pedido);
            var comando = BaseDatos.GetStoredProcCommand("SelectPedido");

            BaseDatos.AddInParameter(comando, "IdPedido", DbType.Int32, idPedido);

            using (var lector = BaseDatos.ExecuteReader(comando))
            {
                while (lector.Read())
                {
                    pedido = new Pedido
                    {
                        IdPedido = lector.GetInt32(lector.GetOrdinal("IdPedido")),
                        Cliente = ClienteData.Instancia.Obtener(lector.GetInt32(lector.GetOrdinal("IdCliente"))),
                        Departamento = DepartamentoData.Instancia.Obtener(lector.GetInt32(lector.GetOrdinal("IdDepartamento"))),
                        Documento = lector.GetByte(lector.GetOrdinal("Documento")),
                        Fecha = lector.GetDateTime(lector.GetOrdinal("Fecha")),
                        Cuenta = CuentaData.Instancia.Obtener(lector.GetInt32(lector.GetOrdinal("IdCuenta"))),
                        NroOperacion = lector.GetString(lector.GetOrdinal("NroOperacion")),
                        FechaOperacion = lector.GetDateTime(lector.GetOrdinal("FechaOperacion")),
                        Descripcion = lector.IsDBNull(lector.GetOrdinal("Descripcion")) ? default(string) : lector.GetString(lector.GetOrdinal("Descripcion")),
                        Estado = lector.GetByte(lector.GetOrdinal("Estado")),
                        Autorizador = lector.IsDBNull(lector.GetOrdinal("Autorizador")) ? default(string) : lector.GetString(lector.GetOrdinal("Autorizador")),
                        Envio = EnvioData.Instancia.Obtener(idPedido),
                        Detalle = DetallePedidoData.Instancia.ListarPorIdPedido(idPedido)
                    };
                }
            }
            comando.Dispose();
            return pedido;
        }

        /// <summary>
        /// Saves a record to the Pedido table.
        /// </summary>
        public int Agregar(Pedido pedido)
        {
            using (var connection = BaseDatos.CreateConnection())
            {
                connection.Open();
                using (var transaction = connection.BeginTransaction())
                {
                    try
                    {
                        var comandoPedido = BaseDatos.GetStoredProcCommand("InsertPedido");
                        BaseDatos.AddOutParameter(comandoPedido, "IdPedido", DbType.Int32, 4);
                        BaseDatos.AddInParameter(comandoPedido, "IdCliente", DbType.Int32, pedido.Cliente.IdCliente);
                        BaseDatos.AddInParameter(comandoPedido, "IdDepartamento", DbType.Int32, pedido.Departamento.IdDepartamento);
                        BaseDatos.AddInParameter(comandoPedido, "Documento", DbType.Byte, pedido.Documento);
                        BaseDatos.AddInParameter(comandoPedido, "Fecha", DbType.DateTime, pedido.Fecha);
                        BaseDatos.AddInParameter(comandoPedido, "IdCuenta", DbType.Int32, pedido.Cuenta.IdCuenta);
                        BaseDatos.AddInParameter(comandoPedido, "NroOperacion", DbType.String, pedido.NroOperacion);
                        BaseDatos.AddInParameter(comandoPedido, "FechaOperacion", DbType.DateTime, pedido.FechaOperacion);
                        BaseDatos.AddInParameter(comandoPedido, "Descripcion", DbType.String, pedido.Descripcion);
                        BaseDatos.AddInParameter(comandoPedido, "Estado", DbType.Byte, pedido.Estado);
                        BaseDatos.AddInParameter(comandoPedido, "Autorizador", DbType.String, pedido.Autorizador);

                        var resultado = BaseDatos.ExecuteNonQuery(comandoPedido, transaction);
                        if (resultado == 0) throw new Exception("Error al Agregar Pedido.");
                        var idPedido = (int)BaseDatos.GetParameterValue(comandoPedido, "IdPedido");

                        var comandoDetalle = BaseDatos.GetStoredProcCommand("InsertDetallePedido");
                        BaseDatos.AddInParameter(comandoDetalle, "IdPedido", DbType.Int32);
                        BaseDatos.AddInParameter(comandoDetalle, "IdProducto", DbType.Int32);
                        BaseDatos.AddInParameter(comandoDetalle, "Cantidad", DbType.Int32);
                        BaseDatos.AddInParameter(comandoDetalle, "Precio", DbType.Currency);
                        BaseDatos.AddInParameter(comandoDetalle, "Descuento", DbType.Decimal);

                        foreach (var detalle in pedido.Detalle)
                        {
                            BaseDatos.SetParameterValue(comandoDetalle, "IdPedido", idPedido);
                            BaseDatos.SetParameterValue(comandoDetalle, "IdProducto", detalle.Producto.IdProducto);
                            BaseDatos.SetParameterValue(comandoDetalle, "Cantidad", detalle.Cantidad);
                            BaseDatos.SetParameterValue(comandoDetalle, "Precio", detalle.Precio);
                            BaseDatos.SetParameterValue(comandoDetalle, "Descuento", detalle.Descuento);

                            resultado = BaseDatos.ExecuteNonQuery(comandoDetalle, transaction);
                            if (resultado == 0) throw new Exception("Error al Agregar DetallePedido.");
                        }

                        if (pedido.Envio != null)
                        {
                            var comandoEnvio = BaseDatos.GetStoredProcCommand("InsertEnvio");
                            BaseDatos.AddInParameter(comandoEnvio, "IdPedido", DbType.Int32, idPedido);
                            BaseDatos.AddInParameter(comandoEnvio, "Costo", DbType.Currency, pedido.Envio.Costo);
                            BaseDatos.AddInParameter(comandoEnvio, "FechaEnvio", DbType.DateTime, pedido.Envio.FechaEnvio);
                            BaseDatos.AddInParameter(comandoEnvio, "IdDistrito", DbType.Int32, pedido.Envio.Distrito.IdDistrito);
                            BaseDatos.AddInParameter(comandoEnvio, "Direccion", DbType.String, pedido.Envio.Direccion);
                            BaseDatos.AddInParameter(comandoEnvio, "Comentario", DbType.String, pedido.Envio.Comentario);
                            BaseDatos.AddInParameter(comandoEnvio, "Estado", DbType.Byte, pedido.Envio.Estado);

                            resultado = BaseDatos.ExecuteNonQuery(comandoEnvio, transaction);
                            if (resultado == 0) throw new Exception("Error al Agregar Envio.");
                        }

                        transaction.Commit();
                        comandoPedido.Dispose();
                        comandoDetalle.Dispose();

                        return idPedido;
                    }
                    catch (Exception)
                    {
                        transaction.Rollback();
                        throw;
                    }
                }
            }
        }

        /// <summary>
        /// Updates a record in the Pedido table.
        /// </summary>
        public bool Modificar(Pedido pedido)
        {
            using (var connection = BaseDatos.CreateConnection())
            {
                connection.Open();
                using (var transaction = connection.BeginTransaction())
                {
                    try
                    {
                        var comandoPedido = BaseDatos.GetStoredProcCommand("UpdatePedido");
                        BaseDatos.AddInParameter(comandoPedido, "IdPedido", DbType.Int32, pedido.IdPedido);
                        BaseDatos.AddInParameter(comandoPedido, "IdCliente", DbType.Int32, pedido.Cliente.IdCliente);
                        BaseDatos.AddInParameter(comandoPedido, "IdDepartamento", DbType.Int32, pedido.Departamento.IdDepartamento);
                        BaseDatos.AddInParameter(comandoPedido, "Documento", DbType.Byte, pedido.Documento);
                        BaseDatos.AddInParameter(comandoPedido, "Fecha", DbType.DateTime, pedido.Fecha);
                        BaseDatos.AddInParameter(comandoPedido, "IdCuenta", DbType.Int32, pedido.Cuenta.IdCuenta);
                        BaseDatos.AddInParameter(comandoPedido, "NroOperacion", DbType.String, pedido.NroOperacion);
                        BaseDatos.AddInParameter(comandoPedido, "FechaOperacion", DbType.DateTime, pedido.FechaOperacion);
                        BaseDatos.AddInParameter(comandoPedido, "Descripcion", DbType.String, pedido.Descripcion);
                        BaseDatos.AddInParameter(comandoPedido, "Estado", DbType.Byte, pedido.Estado);
                        BaseDatos.AddInParameter(comandoPedido, "Autorizador", DbType.String, pedido.Autorizador);

                        var resultado = BaseDatos.ExecuteNonQuery(comandoPedido, transaction);
                        if (resultado == 0) throw new Exception("Error al Modificar Pedido.");

                        var comandoDetalle = BaseDatos.GetStoredProcCommand("DeleteDetallePedidoByIdPedido");
                        BaseDatos.AddInParameter(comandoDetalle, "IdPedido", DbType.Int32, pedido.IdPedido);
                        resultado = BaseDatos.ExecuteNonQuery(comandoDetalle);
                        if (resultado == 0) throw new Exception("Error al Eliminar Detalle de Pedido.");

                        comandoDetalle = BaseDatos.GetStoredProcCommand("InsertDetallePedido");
                        BaseDatos.AddInParameter(comandoDetalle, "IdPedido", DbType.Int32);
                        BaseDatos.AddInParameter(comandoDetalle, "IdProducto", DbType.Int32);
                        BaseDatos.AddInParameter(comandoDetalle, "Cantidad", DbType.Int32);
                        BaseDatos.AddInParameter(comandoDetalle, "Precio", DbType.Currency);
                        BaseDatos.AddInParameter(comandoDetalle, "Descuento", DbType.Decimal);

                        foreach (var detalle in pedido.Detalle)
                        {
                            BaseDatos.SetParameterValue(comandoDetalle, "IdPedido", pedido.IdPedido);
                            BaseDatos.SetParameterValue(comandoDetalle, "IdProducto", detalle.Producto.IdProducto);
                            BaseDatos.SetParameterValue(comandoDetalle, "Cantidad", detalle.Cantidad);
                            BaseDatos.SetParameterValue(comandoDetalle, "Precio", detalle.Precio);
                            BaseDatos.SetParameterValue(comandoDetalle, "Descuento", detalle.Descuento);

                            resultado = BaseDatos.ExecuteNonQuery(comandoDetalle, transaction);
                            if (resultado == 0) throw new Exception("Error al Agregar DetallePedido.");
                        }

                        if (pedido.Envio != null)
                        {
                            if (pedido.Envio.Pedido == null)
                            {
                                var comandoEnvio = BaseDatos.GetStoredProcCommand("InsertEnvio");
                                BaseDatos.AddInParameter(comandoEnvio, "IdPedido", DbType.Int32, pedido.IdPedido);
                                BaseDatos.AddInParameter(comandoEnvio, "Costo", DbType.Currency, pedido.Envio.Costo);
                                BaseDatos.AddInParameter(comandoEnvio, "FechaEnvio", DbType.DateTime, pedido.Envio.FechaEnvio);
                                BaseDatos.AddInParameter(comandoEnvio, "IdDistrito", DbType.Int32, pedido.Envio.Distrito.IdDistrito);
                                BaseDatos.AddInParameter(comandoEnvio, "Direccion", DbType.String, pedido.Envio.Direccion);
                                BaseDatos.AddInParameter(comandoEnvio, "Comentario", DbType.String, pedido.Envio.Comentario);
                                BaseDatos.AddInParameter(comandoEnvio, "Estado", DbType.Byte, pedido.Envio.Estado);

                                resultado = BaseDatos.ExecuteNonQuery(comandoEnvio, transaction);
                                if (resultado == 0) throw new Exception("Error al Agregar Envio.");
                            }
                            else
                            {
                                var comandoEnvio = BaseDatos.GetStoredProcCommand("UpdateEnvio");
                                BaseDatos.AddInParameter(comandoEnvio, "IdPedido", DbType.Int32, pedido.IdPedido);
                                BaseDatos.AddInParameter(comandoEnvio, "Costo", DbType.Currency, pedido.Envio.Costo);
                                BaseDatos.AddInParameter(comandoEnvio, "FechaEnvio", DbType.DateTime, pedido.Envio.FechaEnvio);
                                BaseDatos.AddInParameter(comandoEnvio, "IdDistrito", DbType.Int32, pedido.Envio.Distrito.IdDistrito);
                                BaseDatos.AddInParameter(comandoEnvio, "Direccion", DbType.String, pedido.Envio.Direccion);
                                BaseDatos.AddInParameter(comandoEnvio, "Comentario", DbType.String, pedido.Envio.Comentario);
                                BaseDatos.AddInParameter(comandoEnvio, "Estado", DbType.Byte, pedido.Envio.Estado);

                                resultado = BaseDatos.ExecuteNonQuery(comandoEnvio);
                                if (resultado == 0) throw new Exception("Error al Modificar Envio.");
                            }
                        }
                        else
                        {
                            var comandoEnvio = BaseDatos.GetStoredProcCommand("DeleteEnvio");
                            BaseDatos.AddInParameter(comandoEnvio, "IdPedido", DbType.Int32, pedido.IdPedido);
                            resultado = BaseDatos.ExecuteNonQuery(comandoEnvio);
                            if (resultado == 0) throw new Exception("Error al Eliminar Envio.");
                        }

                        transaction.Commit();
                        comandoPedido.Dispose();
                        comandoDetalle.Dispose();
                        return true;
                    }
                    catch (Exception)
                    {
                        transaction.Rollback();
                        throw;
                    }
                }
            }
        }

        public void ModificarEstado(int idPedido, byte estado, string autorizador)
        {
            var comando = BaseDatos.GetStoredProcCommand("UpdateEstadoPedido");
            BaseDatos.AddInParameter(comando, "IdPedido", DbType.Int32, idPedido);
            BaseDatos.AddInParameter(comando, "Estado", DbType.Byte, estado);
            BaseDatos.AddInParameter(comando, "Autorizador", DbType.String, autorizador);

            var resultado = BaseDatos.ExecuteNonQuery(comando);
            if (resultado == 0) throw new Exception("Error al Modificar Estado de Pedido.");
            comando.Dispose();
        }

        /// <summary>
        /// Deletes a record from the Pedido table by its primary key.
        /// </summary>
        public bool Eliminar(int idPedido)
        {
            var comando = BaseDatos.GetStoredProcCommand("DeletePedido");
            BaseDatos.AddInParameter(comando, "IdPedido", DbType.Int32, idPedido);

            var resultado = BaseDatos.ExecuteNonQuery(comando);
            if (resultado == 0) throw new Exception("Error al Eliminar Pedido.");
            comando.Dispose();
            return true;
        }

        /// <summary>
        /// Deletes a record from the Pedido table by a foreign key.
        /// </summary>
        public void EliminarPorIdCliente(int idCliente)
        {
            var comando = BaseDatos.GetStoredProcCommand("DeletePedidoByIdCliente");
            BaseDatos.AddInParameter(comando, "IdCliente", DbType.Int32, idCliente);

            var resultado = BaseDatos.ExecuteNonQuery(comando);
            if (resultado == 0) throw new Exception("Error al Eliminar Pedido.");
            comando.Dispose();
        }

        /// <summary>
        /// Deletes a record from the Pedido table by a foreign key.
        /// </summary>
        public void EliminarPorIdDepartamento(int idDepartamento)
        {
            var comando = BaseDatos.GetStoredProcCommand("DeletePedidoByIdDepartamento");
            BaseDatos.AddInParameter(comando, "IdDepartamento", DbType.Int32, idDepartamento);

            var resultado = BaseDatos.ExecuteNonQuery(comando);
            if (resultado == 0) throw new Exception("Error al Eliminar Pedido.");
            comando.Dispose();
        }

        /// <summary>
        /// Selects all records from the Pedido table by a foreign key.
        /// </summary>
        public IList<Pedido> ListarPorIdCliente(int idCliente)
        {
            var entidades = new List<Pedido>();
            var comando = BaseDatos.GetStoredProcCommand("SelectPedidoByIdCliente");

            BaseDatos.AddInParameter(comando, "IdCliente", DbType.Int32, idCliente);

            using (var lector = BaseDatos.ExecuteReader(comando))
            {
                while (lector.Read())
                {
                    entidades.Add(new Pedido
                    {
                        IdPedido = lector.GetInt32(lector.GetOrdinal("IdPedido")),
                        Cliente = ClienteData.Instancia.Obtener(lector.GetInt32(lector.GetOrdinal("IdCliente"))),
                        Departamento = DepartamentoData.Instancia.Obtener(lector.GetInt32(lector.GetOrdinal("IdDepartamento"))),
                        Documento = lector.GetByte(lector.GetOrdinal("Documento")),
                        Fecha = lector.GetDateTime(lector.GetOrdinal("Fecha")),
                        Cuenta = CuentaData.Instancia.Obtener(lector.GetInt32(lector.GetOrdinal("IdCuenta"))),
                        NroOperacion = lector.GetString(lector.GetOrdinal("NroOperacion")),
                        FechaOperacion = lector.GetDateTime(lector.GetOrdinal("FechaOperacion")),
                        Descripcion = lector.IsDBNull(lector.GetOrdinal("Descripcion")) ? default(string) : lector.GetString(lector.GetOrdinal("Descripcion")),
                        Estado = lector.GetByte(lector.GetOrdinal("Estado")),
                        Autorizador = lector.IsDBNull(lector.GetOrdinal("Autorizador")) ? default(string) : lector.GetString(lector.GetOrdinal("Autorizador"))
                    });
                }
            }
            comando.Dispose();
            return entidades;
        }

        /// <summary>
        /// Selects all records from the Pedido table by a foreign key.
        /// </summary>
        public IList<Pedido> ListarPorIdDepartamento(int idDepartamento)
        {
            var entidades = new List<Pedido>();
            var comando = BaseDatos.GetStoredProcCommand("SelectPedidoByIdDepartamento");

            BaseDatos.AddInParameter(comando, "IdDepartamento", DbType.Int32, idDepartamento);

            using (var lector = BaseDatos.ExecuteReader(comando))
            {
                while (lector.Read())
                {
                    entidades.Add(new Pedido
                    {
                        IdPedido = lector.GetInt32(lector.GetOrdinal("IdPedido")),
                        Cliente = ClienteData.Instancia.Obtener(lector.GetInt32(lector.GetOrdinal("IdCliente"))),
                        Departamento = DepartamentoData.Instancia.Obtener(lector.GetInt32(lector.GetOrdinal("IdDepartamento"))),
                        Documento = lector.GetByte(lector.GetOrdinal("Documento")),
                        Fecha = lector.GetDateTime(lector.GetOrdinal("Fecha")),
                        Cuenta = CuentaData.Instancia.Obtener(lector.GetInt32(lector.GetOrdinal("IdCuenta"))),
                        NroOperacion = lector.GetString(lector.GetOrdinal("NroOperacion")),
                        FechaOperacion = lector.GetDateTime(lector.GetOrdinal("FechaOperacion")),
                        Descripcion = lector.IsDBNull(lector.GetOrdinal("Descripcion")) ? default(string) : lector.GetString(lector.GetOrdinal("Descripcion")),
                        Estado = lector.GetByte(lector.GetOrdinal("Estado")),
                        Autorizador = lector.IsDBNull(lector.GetOrdinal("Autorizador")) ? default(string) : lector.GetString(lector.GetOrdinal("Autorizador"))
                    });
                }
            }
            comando.Dispose();
            return entidades;
        }

        #endregion
    }
}
